/*
 * @Author: yangni 920556271@qq.com
 * @Date: 2024-03-11 16:16:13
 * @LastEditors: yangni 920556271@qq.com
 * @LastEditTime: 2024-05-16 11:27:27
 * @FilePath: /yt-devops/lib/pages/devices/device_controll/device_set_widget/device_set_widget.dart
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
import 'dart:convert';

import 'package:bruno/bruno.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:sprintf/sprintf.dart';
import 'package:yt_devops/components/device/device_picker.dart';
import 'package:yt_devops/components/device/device_switch.dart';
import 'package:yt_devops/components/gaps.dart';
import 'package:yt_devops/components/yt_input.dart';
import 'package:yt_devops/components/yt_text_style.dart';
import 'package:yt_devops/configs.dart';
import 'package:yt_devops/model/data_type_config.dart';
import 'package:yt_devops/model/device_obj/authority_config_model.dart';
import 'package:yt_devops/model/device_obj/product_config_model.dart';
import 'package:yt_devops/model/device_obj/prompt_rule_model.dart';
import 'package:yt_devops/model/device_obj/properties_model.dart';
import 'package:yt_devops/model/device_obj/service_config_model.dart';
import 'package:yt_devops/model/device_obj/spec/arrays_spec.dart';
import 'package:yt_devops/model/device_obj/spec/double_spec.dart';
import 'package:yt_devops/model/device_obj/spec/struct_spec.dart';
import 'package:yt_devops/pages/devices/device_controll/device_controll_utils.dart';
import 'package:yt_devops/pages/devices/device_tab/utils/device_util.dart';
import 'package:yt_devops/pages/site/site_setting/siet_elec_price_setting/add_mains_electricity_price/views/effective_data_widget.dart';
import 'package:yt_devops/resources/str_res_keys.dart';
import 'package:yt_devops/services/device/device_sevices.dart';
import 'package:yt_devops/styles/styles.dart';
import 'package:yt_devops/utils/brn_row1_delegate.dart';
import 'package:yt_devops/utils/extensions.dart';
import 'package:yt_devops/utils/func_utils.dart';
import 'package:yt_devops/utils/number_input_limit_formatter.dart';
import 'package:yt_devops/utils/toast_utils.dart';

import 'choose_date_widget.dart';

class DeviceSetWidget extends StatefulWidget {
  final Map<String, dynamic>? data;

  final ServiceConfigModel serviceConfigModel;
  final int deviceId;

  const DeviceSetWidget(
      {super.key,
      this.data,
      required this.serviceConfigModel,
      required this.deviceId});

  @override
  State<DeviceSetWidget> createState() => DeviceSetWidgetState();
}

class DeviceSetWidgetState extends State<DeviceSetWidget> {
  ///
  Map<String, dynamic> dataSource = {};

  ///
  List<PropertiesModel> propertyList = [];

  late BuildContext _context;

  @override
  void initState() {
    propertyList = getPropertList(widget.serviceConfigModel);
    dataSource = initData(widget.data ?? {});
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    _context = context;
    return ListView.builder(
      shrinkWrap: true,
      itemBuilder: (context, index) {
        PropertiesModel property = propertyList[index];
        return itemComponent(
          widget.serviceConfigModel,
          property,
        );
      },
      itemCount: propertyList.length,
    );
  }

  List<PropertiesModel> getPropertList(ServiceConfigModel serviceConfigModel) {
    List children = serviceConfigModel.children ?? [];
    List<PropertiesModel> list = [];
    for (var element in children) {
      if (element is Map<String, dynamic>) {
        if (element['type'] == DeviceConfigType.property.name) {
          list.add(PropertiesModel.fromJson(element));
        }
      } else if (element is PropertiesModel) {
        list.add(element);
      }
    }
    return list;
  }

  initData(Map<String, dynamic> data) {
    Map<String, dynamic> dataMap = {};

    var keys = data.keys.toList();
    for (var element in propertyList) {
      var id = DeviceControllUtils.getPropertyId(element);
      if (keys.contains(id)) {
        dataMap.putIfAbsent(id, () {
          if (element.dataType?.type == DataTypeConfigEnum.array ||
              element.dataType?.type == DataTypeConfigEnum.struct) {
            return jsonDecode(data[id]);
          } else {
            return data[id];
          }
          // return data[element.id];
        });
      }
    }
    return dataMap;
  }

  void sendCommand(
      {BuildContext? currentContext,

      ///确认回调
      Function(bool success, {Map<String, dynamic>? data})? confirmHandler}) {
    if (!DeviceControllUtils.validate(propertyList, dataSource)) {
      return;
    }
    DeviceControllUtils.remoteSetting(
      context: currentContext ?? context,
      deviceId: getRemoteSettingDeviceId,
      service: widget.serviceConfigModel,
      data: dataSource,
      confirmHandler: confirmHandler,
    );
  }

  int get getRemoteSettingDeviceId {
    if (widget.serviceConfigModel.deviceId != null) {
      return widget.serviceConfigModel.deviceId!;
    } else {
      return widget.deviceId;
    }
  }

  itemComponent(
      ServiceConfigModel serviceConfigModel, PropertiesModel? property,
      {int? dataIndex}) {
    if (property == null) {
      return Gaps.empty;
    }
    Widget comp;
    if (dataSource[DeviceControllUtils.getPropertyId(property)] == null &&
        property.defaultValue != null) {
      dataSource[DeviceControllUtils.getPropertyId(property)] =
          property.defaultValue;
    }
    var value = property.dataType?.type != DataTypeConfigEnum.array &&
            property.dataType?.type != DataTypeConfigEnum.struct &&
            property.dataType?.type != DataTypeConfigEnum.treeSelect
        ? FuncUtils.formatModelValue(
                dataSource[DeviceControllUtils.getPropertyId(property)] ??
                    property.defaultValue,
                property.dataType,
                isShowUnit: false,
                defaultValue: '')
            .toString()
        : dataSource[DeviceControllUtils.getPropertyId(property)];
    bool disabled =
        DeviceControllUtils.isDisabledByExpression(property, dataSource);
    String? tip = DeviceControllUtils.disableTip(property.tip, disabled);
    bool required = DeviceControllUtils.isPropertyRequired(property);

    var spec = property.dataType?.specs;

    if (spec is DoubleSpec) {
      var number = num.tryParse(value);

      if (number == null) {
      } else if (spec.max != null && number > spec.max! ||
          spec.min != null && number < spec.min!) {
        tip = sprintf(
            ResKey.commonValueRangeTipSentence.tr, [spec.min!, spec.max!]);
      } else {
        tip = null;
      }
    }

    switch (property.dataType?.type) {
      case DataTypeConfigEnum.int:
      case DataTypeConfigEnum.long:
      case DataTypeConfigEnum.double:
      case DataTypeConfigEnum.string:
      case null:
        String unit = '';
        TextInputType keyboardType;
        switch (property.dataType?.type) {
          case DataTypeConfigEnum.int:
            keyboardType = TextInputType.number;
            unit = (property.dataType?.specs as DoubleSpec?)?.unit ?? '';
            break;
          case DataTypeConfigEnum.double:
          case DataTypeConfigEnum.long:
            keyboardType = const TextInputType.numberWithOptions(
                decimal: true, signed: true);
            unit = (property.dataType?.specs as DoubleSpec?)?.unit ?? '';
            break;
          default:
            keyboardType = TextInputType.text;
        }
        comp = YtInput(
          isRequire: required,
          labelText: property.name ?? '',
          errorText: tip,
          enabled: !disabled,
          keyboardType: keyboardType,
          placeholderText: ResKey.plsInput.tr,
          border: const UnderlineInputBorder(
              borderSide: BorderSide(color: Colour.dividerColor)),
          shifxIcon: unitWidget(unit),
          suffixIconConstraints: BoxConstraints(maxWidth: getUnitWidth(unit)),
          inputTextStyle: TextStyles.f14,
          labelTextStyle: TextStyles.f14,
          controller: TextEditingController(text: value?.toString() ?? ''),
          onChanged: (value) {
            updateData(property, value);
          },
        );

        break;

      case DataTypeConfigEnum.enumType:
        comp = enumComponent(property, value,
            promptRule: serviceConfigModel.promptRule,
            disabled: disabled,
            tip: tip);
        break;

      case DataTypeConfigEnum.timestamp:
        comp = DevicePicker(
            pickType: PickTypeEnum.minute,
            model: property,
            required: required,
            value: value,
            disable: disabled,
            tip: tip,
            onTimeChanged: (dateTime, list) {
              var timestamp = dateTime.millisecondsSinceEpoch;
              updateData(property, timestamp);
            });
        break;

      case DataTypeConfigEnum.boolean:
        comp = DeviceSwitch(
          model: property,
          value: value == '0',
          disable: disabled,
          tip: tip,
          onChanged: (ldValue, newValue) {
            updateData(property, newValue == true ? 1 : 0);
          },
        );
        break;

      case DataTypeConfigEnum.treeSelect:
        comp =
            treeSelectComponent(property, value, disabled: disabled, tip: tip);
        break;

      case DataTypeConfigEnum.array:
        comp = arrayComponent(serviceConfigModel, property, value ?? []);
        break;

      case DataTypeConfigEnum.struct:
        var spec = property.dataType!.specs as List<PropertiesModel>?;
        comp = structComponent(spec, value);
        break;

      default:
        comp = Container();
    }
    if (property.dataType?.type != DataTypeConfigEnum.array &&
        property.dataType?.type != DataTypeConfigEnum.struct) {
      return Container(
        color: Colour.white,
        padding: EdgeInsets.symmetric(horizontal: 16.w),
        child: comp,
      );
    } else {
      return comp;
    }
  }

  Widget enumComponent(PropertiesModel property, value,
      {List<PromptRuleModel>? promptRule, bool disabled = false, String? tip}) {
    List<PickModel> list = [];
    (property.dataType?.specs as Map).forEach((key, value) {
      list.add(PickModel(name: value, value: key));
    });

    return DevicePicker(
        model: property,
        list: list,
        value: value,
        required: DeviceControllUtils.isPropertyRequired(property),
        disable: disabled,
        tip: tip,
        onChanged: (PickModel pickModel) {
          updateData(property, pickModel.value);
        });
  }

  Widget treeSelectComponent(PropertiesModel model, value,
      {bool disabled = false, String? tip}) {
    List<PickModel> list = (model.dataType?.specs as List)
        .map((e) => PickModel(name: e['name'], value: e['sn']))
        .toList();
    String snList = list.map((e) => e.value).toList().join(',');
    var pickValue;
    if (list.isEmpty) {
      pickValue = FuncUtils.isListEmpty(value)
          ? null
          : value.join(',') == snList
              ? snList
              : value[0];
    }

    return DevicePicker(
        model: model,
        list: list,
        value: pickValue,
        required: model.required == true,
        disable: disabled,
        tip: tip,
        onChanged: (PickModel pickModel) {
          var pickValue = [];
          if (pickModel.value == snList) {
            pickValue = snList.split(',');
          } else {
            pickValue = [pickModel.value];
          }
          updateData(model, pickValue);
        });
  }

  Widget arrayComponent(
      ServiceConfigModel serviceConfigModel, PropertiesModel model, List data) {
    var arraySpec = model.dataType?.specs as ArraysSpec;
    var itemDataType = arraySpec.item;
    return Column(children: [
      ListView.separated(
        shrinkWrap: true,
        physics: const NeverScrollableScrollPhysics(),
        itemBuilder: (context, index) {
          Widget comp;

          if (itemDataType?.type == DataTypeConfigEnum.struct) {
            comp = structComponent(
              itemDataType?.specs as List<PropertiesModel>?,
              data[index],
              dataIndex: index,
              onChanged: (PropertiesModel p, structData) {
                List<String> timeList = [];
                if (p.dataType?.type == DataTypeConfigEnum.timeRange) {
                  int dataindex = -1;
                  for (var i = 0; i < data.length; i++) {
                    if (data[i][p.id] == structData[p.id]) {
                      dataindex = i;
                    } else {
                      timeList.add(data[i][p.id]);
                    }
                  }

                  if (DeviceControllUtils.isTimeOverLap(
                          timeList, structData[p.id]) ==
                      true) {
                    TotastUtils.tip(_context,
                        title: ResKey.timePeriodsOverlap.tr);
                    data[dataindex][p.id] = '';
                    updateData(model, data);
                  }
                  setState(() {});
                }
              },
            );
          } else {
            comp = itemComponent(
                serviceConfigModel, itemDataType?.specs as PropertiesModel?,
                dataIndex: index);
          }

          // return comp;
          return Container(
            color: Colour.white,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Expanded(child: comp),
                deleteIconButton(deleteHandler: () {
                  data.removeAt(index);
                  setState(() {});
                }),
              ],
            ),
          );
        },
        separatorBuilder: (context, index) => Gaps.line,
        itemCount: data.length,
      ),
      Offstage(
        offstage: data.length >= (arraySpec.size ?? 0),
        child: GestureDetector(
          behavior: HitTestBehavior.opaque,
          onTap: () {
            var item = DeviceControllUtils.createNewDataForList(itemDataType!);
            if (item != null) {
              data.add(item);
              updateData(model, data);
              setState(() {});
            }
          },
          child: Container(
            height: 49,
            color: Colour.white,
            child: Row(
              children: [
                Container(
                  padding: EdgeInsets.only(left: 16.w, right: 16.w),
                  child: Image.asset(
                    'assets/images/site_elec_price_add.png',
                    height: 18,
                    width: 18,
                  ),
                ),
                Text(
                  ResKey.add.tr,
                  style: ytTextStyle(
                      fontWeight: FontWeight.normal,
                      color: Colour.primaryColor,
                      fontSize: 14.sp),
                ),
              ],
            ),
          ),
        ),
      )
    ]);
  }

  Widget structComponent(List<PropertiesModel>? properties, Map structData,
      {int? dataIndex, Function(PropertiesModel, Map data)? onChanged}) {
    if (FuncUtils.isListEmpty(properties)) {
      Gaps.empty;
    }
    return Column(
        children: properties!.mapIndexed((e, i) {
      Widget comp;
      if (i == 0 && dataIndex != null) {
        /// 有索引值，那就在第一个属性的name上显示索引
        e = e.copyWith(nameCope: '${e.name}${dataIndex + 1}');
      }
      switch (e.dataType?.type) {
        case DataTypeConfigEnum.double:
        case DataTypeConfigEnum.long:
        case DataTypeConfigEnum.int:
        case DataTypeConfigEnum.string:
          comp = structInputComponent(e, structData);
          break;
        case DataTypeConfigEnum.enumType:
          comp = structEnumPickerComponent(e, structData);
          break;
        case DataTypeConfigEnum.timeRange:
          comp = structTimeRangeComponent(
            e,
            structData,
            onChanged: (start, end) {
              var time = '$start-$end';
              structData[e.id] = time;

              onChanged?.call(e, structData);
            },
          );
          break;
        default:
          comp = Gaps.empty;
      }
      return comp;
    }).toList());
  }

  Widget structEnumPickerComponent(
    PropertiesModel model,
    Map data, {
    Function(dynamic key, dynamic value)? onChanged,
  }) {
    return Container(
      height: 48,
      color: Colour.white,
      child: Row(
        children: [
          Gaps.hGap16,
          Text(
            model.name ?? '',
            style: ytTextStyle(
                fontWeight: FontWeight.normal,
                color: Colour.lightTextColor,
                fontSize: 14),
          ),
          Spacer(),
          GestureDetector(
              behavior: HitTestBehavior.opaque,
              onTap: () {
                var spec = (model.dataType?.specs as Map?) ?? {};

                showBottomPop(_context, spec, model.name ?? '',
                    confirmClick: (key, value) {
                  data[model.id] = key;
                  setState(() {});
                  onChanged?.call(key, value);
                });
              },
              child: Stack(
                alignment: Alignment.centerRight,
                children: [
                  Container(
                    decoration: const BoxDecoration(
                        color: Colour.backgroundColor,
                        borderRadius: BorderRadius.all(Radius.circular(4))),
                    height: 32,
                    width: 106.w,
                    alignment: Alignment.center,
                    child: Text(
                      FuncUtils.formatModelValue(
                              data[DeviceControllUtils.getPropertyId(model)] ??
                                  model.defaultValue,
                              model.dataType,
                              defaultValue: null) ??
                          '',
                      style: ytTextStyle(
                          fontWeight: FontWeight.normal, fontSize: 14),
                    ),
                  ),
                  Positioned(
                    right: 8.w,
                    child: const Icon(Icons.expand_more),
                  )
                ],
              )),
          Container(
            width: 121.w,
          )
        ],
      ),
    );
  }

  structInputComponent(
    PropertiesModel model,
    Map data, {
    Function(String)? onChanged,
  }) {
    DataTypeConfigEnum? type = model.dataType?.type;
    String unit = '';
    TextInputType keyboardType;
    List<TextInputFormatter> inputFormatter = [];
    var spec = model.dataType?.specs as DoubleSpec?;
    if (type != DataTypeConfigEnum.string) {
      keyboardType = const TextInputType.numberWithOptions(decimal: true);
      inputFormatter = [
        NumberInputLimitFormatter(
            max: spec?.max ?? 99999999, min: spec?.min ?? 0)
      ];
    } else {
      keyboardType = TextInputType.text;
    }
    return Container(
      height: 48,
      color: Colour.white,
      child: Row(
        children: [
          Gaps.hGap16,
          SizedBox(
            width: 81.w,
            child: Text(
              model.name ?? '',
              style: ytTextStyle(
                  fontWeight: FontWeight.normal,
                  color: Colour.lightTextColor,
                  fontSize: 14),
            ),
          ),
          Spacer(),
          Container(
            decoration: const BoxDecoration(
                color: Colour.backgroundColor,
                borderRadius: BorderRadius.all(Radius.circular(4))),
            height: 32,
            width: 106.w,
            alignment: Alignment.center,
            child: TextField(
              textAlignVertical: TextAlignVertical.center,
              textAlign: TextAlign.center,
              style: const TextStyle(fontSize: 14, color: Colour.mainTextColor),
              controller:
                  TextEditingController(text: data[model.id]?.toString() ?? ''),
              keyboardType: keyboardType,
              inputFormatters: inputFormatter,
              decoration: const InputDecoration(
                border: InputBorder.none,
                contentPadding: EdgeInsets.only(bottom: 13),
                enabled: true,
                hintStyle: TextStyle(
                    fontSize: 14.0,
                    color: Colour.lightTextColor,
                    fontWeight: FontWeight.normal),
              ),
              onChanged: (String str) {
                data[model.id] = str;
                onChanged?.call(str);
              },
            ),
          ),
          Container(
            padding: EdgeInsets.only(left: 4.w),
            width: 121.w,
            child: Text(
              unit,
              style: ytTextStyle(fontWeight: FontWeight.normal, fontSize: 14),
            ),
          )
        ],
      ),
    );
  }

  structTimeRangeComponent(
    PropertiesModel model,
    Map data, {
    Function(String, String)? onChanged,
  }) {
    List<String> range = [];
    var timeRange = data[model.id];
    if (!FuncUtils.isEmpty(timeRange) && timeRange!.contains('-')) {
      range = timeRange.split('-');
    } else {
      range = ['', ''];
    }
    return ChooseDateWidget(
      _context,
      model.name ?? '',
      range[0],
      range[1],
      (startDateTime, endDateTime) {
        data[model.id] = '$startDateTime-$endDateTime';
        setState(() {});
        onChanged?.call(startDateTime, endDateTime);
      },
      () {},
      titleWidth: 85.w,
      isSHowOperateButton: false,
      isShowHour: true,
      isEdit: true,
    );
  }

  deleteIconButton({Function? deleteHandler}) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () {
        deleteHandler?.call();
      },
      child: Padding(
        padding: EdgeInsets.fromLTRB(16.w, 18, 16.w, 0),
        child: Image.asset(
          'assets/images/site_elec_price_delete.png',
          height: 18,
          width: 18,
        ),
      ),
    );
  }

  Widget? unitWidget(String? unit) {
    return FuncUtils.isEmpty(unit)
        ? null
        : Container(
            alignment: Alignment.center,
            child: Text(
              unit!,
            ));
  }

  double getUnitWidth(String? unit) {
    return FuncUtils.calculateTextHeight(
            Get.context!, unit ?? '', FontWeight.normal, 14.sp, 100, 1) +
        2;
  }

  ///弹出底部弹窗
  showBottomPop(BuildContext context, Map enumMap, String title,
      {void Function(dynamic key, dynamic value)? confirmClick}) {
    var keys = enumMap.keys.toList();
    var values = enumMap.values.map((e) => e.toString()).toList();
    BrnMultiDataPicker(
      context: context,
      title: title,
      confirmClick: (indexList) {
        if (indexList.isNotEmpty) {
          var index = indexList[0];
          confirmClick?.call(keys[index], values[index]);
        }
      },
      delegate: Brn1RowDelegate(values),
    ).show();
  }

  updateData(PropertiesModel property, value) {
    var key = DeviceControllUtils.getPropertyId(property);
    if (dataSource.containsKey(key)) {
      dataSource[key] = value;
    } else {
      dataSource.putIfAbsent(key, () => value);
    }

    setState(() {});
  }
}
